package com.wh.hotel.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.wh.hotel.convert.HotelStructConvert;
import com.wh.hotel.dao.*;
import com.wh.hotel.enums.YesNoEnum;
import com.wh.hotel.enums.base.HotelOperateTypeEnum;
import com.wh.hotel.model.*;
import com.wh.hotel.param.HotelQueryRoomParam;
import com.wh.hotel.response.HotelRoomDetailResponse;
import com.wh.hotel.service.HotelRoomTypeCalendarService;
import com.wh.hotel.service.HotelRoomTypeGoodsService;
import com.wh.hotel.service.HotelRoomTypeService;
import com.wh.hotel.util.PageResultUtil;
import com.wh.hotel.vo.HotelRoomTypeVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author luweije
 */
@Slf4j
@Service
public class HotelRoomTypeServiceImpl implements HotelRoomTypeService {

    @Resource
    private HotelRoomTypeDao hotelRoomTypeDao;

    @Resource
    private HotelRoomTypeRelationDao hotelRoomTypeRelationDao;

    @Resource
    private HotelBaseInfoDao hotelBaseInfoDao;

    @Resource
    private HotelBookRuleDao hotelBookRuleDao;

    @Resource
    private HotelRoomTypeGoodsDao hotelRoomTypeGoodsDao;

    @Resource
    private HotelStructConvert hotelStructConvert;

    @Resource
    private HotelRoomTypeGoodsService hotelRoomTypeGoodsService;

    @Resource
    private HotelRoomTypeCalendarService hotelRoomTypeCalendarService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public HotelRoomType addRoomType(HotelRoomTypeVo param) {
        HotelBaseInfo hotelBaseInfo = hotelBaseInfoDao.selectById(param.getHotelId());
        Preconditions.checkNotNull(hotelBaseInfo, "酒店不存在");
        HotelBookRule hotelBookRule = hotelBookRuleDao.selectById(param.getBookRuleId());
        Preconditions.checkNotNull(hotelBookRule, "预订规则不存在");
        Preconditions.checkArgument(param.getBasePrice() > 0, "房型基准价必须大于0");
        Preconditions.checkArgument(param.getPhysicalStock() >= 0, "房型物理库存必须大于等于0");
        Preconditions.checkArgument(param.getBreakfastPrice() > 0, "房型早餐单价必须大于0");
        HotelRoomType hotelRoomType = hotelStructConvert.convertToHotelRoomType(param);
        if (param.getRoomTypeId() == null) {
            // 新增房型
            Preconditions.checkArgument(hotelRoomTypeDao.insert(hotelRoomType), "插入房型数据失败");
            addRoomTypeRelation(param, hotelRoomType.getId());
            hotelRoomTypeGoodsService.addGoods(hotelRoomType.getId(), hotelBookRule);
            return hotelRoomType;
        }
        // 修改
        hotelRoomType.setId(param.getRoomTypeId());
        HotelRoomType oldHotelRoomType = hotelRoomTypeDao.selectById(param.getRoomTypeId());
        Preconditions.checkArgument(hotelRoomTypeDao.update(hotelRoomType), "更新房型数据失败");
        HotelRoomTypeRelation hotelRoomTypeRelation = hotelStructConvert.convertToHotelRoomTypeRelation(param);
        boolean isDiffPrice = isDiffBasePrice(hotelRoomTypeRelation);
        Preconditions.checkArgument(hotelRoomTypeRelationDao.updateByRoomTypeId(hotelRoomTypeRelation), "更新酒店房型关系失败");
        if (isDiffPrice) {
            hotelRoomTypeCalendarService.roomTypeBasePriceChange(hotelRoomTypeRelation.getHotelId(), hotelRoomTypeRelation.getRoomTypeId());
        }
        if (!Objects.equals(oldHotelRoomType.getBookRuleId(), param.getBookRuleId())) {
            hotelRoomTypeGoodsService.updateGoodsForRuleChange(hotelRoomType.getId(), hotelBookRule);
        }
        return hotelRoomType;
    }

    private boolean isDiffBasePrice(HotelRoomTypeRelation newRelation) {
        List<HotelRoomTypeRelation> relationList = hotelRoomTypeRelationDao.selectByRoomTypeId(newRelation.getRoomTypeId());
        HotelRoomTypeRelation oldRelation = relationList.get(0);

        return !Objects.equals(oldRelation.getBasePrice(), newRelation.getBasePrice());
    }

    private void addRoomTypeRelation(HotelRoomTypeVo param, Long roomTypeId) {
        HotelRoomTypeRelation hotelRoomTypeRelation = hotelStructConvert.convertToHotelRoomTypeRelation(param);
        hotelRoomTypeRelation.setRoomTypeId(roomTypeId);
        hotelRoomTypeRelation.setRelationStatus(YesNoEnum.NO.getCode());
        Preconditions.checkArgument(hotelRoomTypeRelationDao.insert(hotelRoomTypeRelation), "插入酒店房型关系数据失败");
        hotelRoomTypeCalendarService.roomTypeBasePriceChange(hotelRoomTypeRelation.getHotelId(), roomTypeId);
    }

    @Override
    public boolean updateRoomTypeStatus(HotelRoomTypeVo hotelRoomTypeVo) {
        HotelOperateTypeEnum operateTypeEnum = HotelOperateTypeEnum.parse(hotelRoomTypeVo.getOperateType());
        return hotelRoomTypeRelationDao.updateRelationStatus(hotelRoomTypeVo.getRoomTypeId(), operateTypeEnum);
    }

    @Override
    public IPage<HotelRoomDetailResponse> queryListRoomInfo(HotelQueryRoomParam param) {
        IPage<HotelRoomTypeRelation> relationPage = hotelRoomTypeRelationDao.selectPageByHotelId(param.getHotelId(), param);
        IPage<HotelRoomDetailResponse> responsePage = new Page<>();
        PageResultUtil.copyPageInfo(relationPage, responsePage);
        List<HotelRoomTypeRelation> records = relationPage.getRecords();
        List<Long> roomTypeIdList = records.stream().map(HotelRoomTypeRelation::getRoomTypeId).collect(Collectors.toList());
        List<HotelRoomDetailResponse> responseList = createRoomDetail(roomTypeIdList, records);
        responsePage.setRecords(responseList);
        return responsePage;
    }

    @Override
    public HotelRoomDetailResponse queryDetailRoomInfo(HotelQueryRoomParam param) {
        List<HotelRoomTypeRelation> relationList = hotelRoomTypeRelationDao.selectByRoomTypeId(param.getRoomTypeId());
        if (CollectionUtils.isEmpty(relationList)) {
            return null;
        }
        List<HotelRoomDetailResponse> roomDetailList = createRoomDetail(Lists.newArrayList(param.getRoomTypeId()), relationList);
        HotelRoomDetailResponse response = roomDetailList.get(0);
        List<HotelRoomTypeGoods> goodsList = hotelRoomTypeGoodsDao.selectByRoomTypeId(param.getRoomTypeId());
        response.setRoomGoodsDetailVoList(
                goodsList.stream()
                        .map(goods -> hotelStructConvert.convertToRoomGoodsDetailVo(goods))
                        .collect(Collectors.toList())
        );
        return response;
    }

    private List<HotelRoomDetailResponse> createRoomDetail(List<Long> roomTypeIdList, List<HotelRoomTypeRelation> relationList) {
        // 查房型
        Map<Long, HotelRoomType> roomTypeIdMap = hotelRoomTypeDao.selectBatchById(roomTypeIdList)
                .stream().collect(Collectors.toMap(HotelRoomType::getId, a -> a, (k1, k2) -> k1));
        List<HotelRoomDetailResponse> responseList = Lists.newArrayList();
        for (HotelRoomTypeRelation record : relationList) {
            HotelRoomDetailResponse response = hotelStructConvert.convertToHotelRoomDetailResponse(record);
            HotelRoomType hotelRoomType = roomTypeIdMap.get(record.getRoomTypeId());
            hotelStructConvert.copyFieldToHotelRoomDetailResponse(response, hotelRoomType);
            response.setRoomTypeId(record.getRoomTypeId());
            responseList.add(response);
        }
        return responseList;
    }
}
